﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;
using System.Diagnostics;

namespace DARE
{
    /// <summary>
    /// Everything displayed by the CHUDSystem is a widget. Everything inherits this class.
    /// This is the base class for every GUI element.
    /// </summary>
    [AttrTypeDependentSerializable(typeof(CWidgetData))]
    public class CWidget
    {
        #region identifier

        public delegate void eventMethod(CWidget sender);

        #endregion

        #region fields

        protected static int m_nbClones;  

        protected CWidget m_parent;

        protected Vector2 m_position = Vector2.Zero;
        protected Vector2 m_size = Vector2.One;
        protected CHudTexture m_texture;
        protected Dictionary<string, CWidget> m_children;
        protected bool m_isFixedSize = true;
        protected bool m_isVisible = false;
        protected string m_name;
        protected SPositionsTBLR m_margins;
        protected bool m_isMoving;

        protected Rectangle m_rect;

        protected CWidget m_activatedCWidget = null;
        protected CWidget m_selectedWidget = null;


        #endregion

        #region properties

        public Game Game { get; set; }

        public CHUDSystem HUD { get; set; }

        public SpriteBatch SpriteBatch { get; set; }

        public bool WasMouseOver { get; set; }

        public bool IsClickable { get; set; }

        /// <summary>
        /// If this value is set to true, we can drop a Vignette on this widget.
        /// Else, we can't.
        /// </summary>
        public bool IsContainer { get; set; }

        public virtual Color Color { get; set; }

        /// <summary>
        /// Allows to use another rectangle than the one created by setting the position and the size for this widget.
        /// </summary>
        public virtual bool UseDrawableRect { get; set; }

        /// <summary>
        /// A rectangle enabled by UseDrawableRect to draw this widget in another rectangle than the one created by setting the position and the size.
        /// </summary>
        public virtual Rectangle DrawableRect { get; set; }

        /// <summary>
        /// If this value is set to true, when a Vignette will be dropped on this widget, this widget will
        /// place the vignette in the containers that this widget has as children.
        /// for example, the CBook class is a container group, when we drop a Vignette on it, it places the vignette
        /// in one of its CInventory.
        /// </summary>
        public bool IsContainerGroup { get; set; }

        /// <summary>
        /// If the widget is not active, it will not raise any event
        /// </summary>
        public bool IsActive { get; set; }

        /// <summary>
        /// Represents the parent of this Widget.
        /// If the parent is null, the widget is a root element and the HUDSystem calls its update/draw functions.
        /// Else, it is the parent of this widget that calls those functions.
        /// When you change the parent of a widget, if the position and the size are relatives (values between 0 and 1), the
        /// size will be changed automatically.
        /// </summary>
        public virtual CWidget Parent
        {
            get { return m_parent; }
            set
            {
                if (this.m_parent != null && (this.m_parent.Children.ContainsValue(this) || m_parent.Children.ContainsKey(m_name)))
                    this.m_parent.Children.Remove(this.m_name);
                this.m_parent = value;
                if (this.m_parent != null && !this.m_parent.Children.ContainsKey(this.m_name))
                    this.m_parent.Children.Add(this.m_name, this);
                if (m_parent == null)
                    return;
                if (m_rect == null)
                    m_rect = new Rectangle();
                this.m_parent.giveSubRect(this.m_position, this.m_size, ref m_rect);
                if (m_texture != null)
                    m_texture.Rectangle = m_rect;
                if (m_parent != null)
                    HUD = m_parent.HUD;
            }
        }

        /// <summary>
        /// If the value is between 0 and 1, the position is a percentage of the size of the parent
        /// (if the parent is null, the value is a percentage of the window).
        /// The value is relative to its parent.
        /// </summary>
        public virtual Vector2 Position
        {
            get { return m_position; }
            set
            {
                if (this.m_parent != null)
                    this.m_parent.giveSubRect(value, this.m_size, ref m_rect);
                else
                {
                    int width;
                    int height;
                    int x;
                    int y;
                    if (m_size.X <= 1 && m_size.Y <= 1)
                    {
                        width =  (int)(this.m_size.X * Game.GraphicsDevice.Viewport.Width);
                        height = (int)(this.m_size.Y * Game.GraphicsDevice.Viewport.Height);
                    }
                    else
                    {
                        width =  (int)(this.m_size.X);
                        height = (int)(this.m_size.Y);
                    }

                    if (value.X <= 1 && value.Y <= 1)
                    {
                        x = (int)(value.X * Game.GraphicsDevice.Viewport.Width);
                        y = (int)(value.Y * Game.GraphicsDevice.Viewport.Height);
                    }
                    else
                    {
                        x = (int)value.X;
                        y = (int)value.Y;
                    }
                    m_rect = new Rectangle(x, y, width, height);
                }
                if (m_texture != null)
                    m_texture.Rectangle = m_rect;
                foreach (CWidget child in m_children.Values)
                    child.Actualize();
                m_position = value;
            }
        }

        /// <summary>
        /// If the value is between 0 and 1, the size is a percentage of the size of the parent
        /// (if the parent is null, the value is a percentage of the window).
        /// Else, the value is absolute.
        /// </summary>
        public virtual Vector2 Size
        {
            get { return m_size; }
            set
            {
                if (this.m_size != value)
                {
                    if (this.m_parent != null)
                        this.m_parent.giveSubRect(this.m_position, value, ref m_rect);
                    else
                    {

                        int width;
                        int height;
                        int x;
                        int y;
                        if (m_position.X <= 1 && m_position.Y <= 1)
                        {
                            x = (int)(this.m_position.X * Game.GraphicsDevice.Viewport.Width);
                            y = (int)(this.m_position.Y * Game.GraphicsDevice.Viewport.Height);
                        }
                        else
                        {
                            x = (int)(this.m_position.X);
                            y = (int)(this.m_position.Y);
                        }

                        if (value.X <= 1 && value.Y <= 1)
                        {
                            width = (int)(value.X * Game.GraphicsDevice.Viewport.Width);
                            height = (int)(value.Y * Game.GraphicsDevice.Viewport.Height);
                        }
                        else
                        {
                            width = (int)value.X;
                            height = (int)value.Y;
                        }
                        m_rect = new Rectangle(x, y, width, height);
                    }
                    if (m_texture != null)
                        m_texture.Rectangle = m_rect;
                    foreach (CWidget child in m_children.Values)
                        child.Actualize();
                        //child.Size += (value - this.m_size);
                    m_size = value;
                }
            }
        }

        /// <summary>
        /// Represents the texture of the widget.
        /// For an animated widget, the animation state is contained in this CHUDTexture
        /// </summary>
        public virtual CHudTexture Texture
        {
            get { return this.m_texture; }
            set
            {
                this.m_texture = value;
            }
        }

        public virtual Dictionary<string, CWidget> Children { get { return this.m_children; } }

        //public virtual bool IsFixedSize { get { return m_isFixedSize; } }

        /// <summary>
        /// If the widget isn't visible, it can always raise any event.
        /// To avoid catching events, see the IsActive property.
        /// If the widget isn't visible, it isn't updated.
        /// </summary>
        public virtual bool IsVisible { get { return m_isVisible; } }

        public virtual bool IsLeftClicked { get; set; }

        public virtual bool IsRightClicked { get; set; }

        public virtual bool IsMiddleClicked { get; set; }

        public virtual string Name
        {
            get { return m_name; }
            set
            {
                if (this.m_name != value)
                {
                    if (this.m_parent != null && this.m_parent.Children[value] == null)
                    {
                        this.m_parent.Children.Add(value, this);
                        this.m_parent.Children.Remove(this.m_name);
                        this.m_name = value;
                    }
                    else if (this.m_parent == null)
                        this.m_name = value;
                }
            }
        }

        /// <summary>
        /// Gets/Sets the margins for the children of this widget.
        /// (if a children's position is set to {0, 0} and the margins of this widget to {5, 5},
        /// the position of its children will be {5, 5} + the position of this widget).
        /// </summary>
        public virtual SPositionsTBLR Margins
        {
            get { return this.m_margins; }
            set
            {
                if (m_margins != value)
                {
                    this.m_margins = value;
                    foreach (CWidget child in m_children.Values)
                    {
                        Rectangle tmp = child.Rect;
                        this.giveSubRect(child.Position, child.Size, ref tmp);
                        child.Rect = tmp;
                        m_texture.Rectangle = m_rect;
                    }
                }
            }
        }

        /// <summary>
        /// Gets/Sets the rectangle where the widget will be draw.
        /// This value is automatically set when the position and/or the size are set.
        /// If you plan to use a different rectangle than the one automatically created,
        /// we recommand you to use the UseDrawableRect and DrawableRect properties.
        /// </summary>
        /// <seealso cref="UseDrawableRect"/>
        /// <seealso cref="DrawableRect"/>
        public virtual Rectangle Rect
        {
            get { return m_rect; }
            set
            {
                m_rect = value;
                if (m_texture != null)
                    m_texture.Rectangle = m_rect;
                foreach (CWidget child in m_children.Values)
                    child.Actualize();
            }
        }

        protected virtual Nullable<Vector2> InitialPosition { get; set; }

        /// <summary>
        /// [Deprecated] Gets/Sets the AlwaysOnTop value. Can be erroneous
        /// </summary>
        public virtual bool IsAlwaysOnTop { get; set; }
        /// <summary>
        /// Gets the current selected widget, directly child of this widget.
        /// </summary>
        public virtual CWidget SelectedCWidget { get { return m_selectedWidget; } }
        /// <summary>
        /// Returns true if the widget is currently moving.
        /// </summary>
        public virtual bool IsMoving { get { return m_isMoving; } }


        public event eventMethod OnClick;
        public event eventMethod OnRelease;
        public event eventMethod OnMouseOverEnter;
        public event eventMethod OnMouseOverExit;


        /// <summary>
        /// Allow the widget to consume the inputs.
        /// If this is set to true, when this widget will catch an input it will clear the event list.
        /// </summary>
        /// <seealso cref="CMouse"/>
        /// <seealso cref="CKeyboard"/>
        public virtual bool IsInputConsumer { get; set; }

        /// <summary>
        /// Gets/Sets the highlight status of this widget.
        /// </summary>
        public bool IsHighLighted { get; set; }

        /// <summary>
        /// Gets/Sets the color of the highlight
        /// </summary>
        public Color HLColor { get; set; }

        #endregion

        #region ctor

        /// <summary>
        /// Constructs the widget and attach it to the HUDSystem and to its parent (if not null).
        /// </summary>
        /// <param name="hud">The HUDSystem to be attached to</param>
        /// <param name="parent">The parent of this widget</param>
        /// <param name="name">The name of the widget. It MUST be unique. We can get this widget by this name.</param>
        /// <seealso cref="CHUDSystem"/>
        public CWidget(CHUDSystem hud, CWidget parent, string name)
        {
            this.m_name = name;
            this.Game = CDare.Instance.Game;
            this.SpriteBatch = hud.SpriteBatch;
            this.m_parent = parent;
            Texture = new CHudTexture();
            IsInputConsumer = true;
            Debug.Assert(hud != null, "The hud given in parameters mustn't be null!");
            HUD = hud;
            HUD.Add(this);
            HLColor = new Microsoft.Xna.Framework.Color(155, 155, 155, 155);
            initialize();
        }

        /// <summary>
        /// Initializes the widget. This method is called automatically by the constructor so you don't need to call it.
        /// </summary>
        public virtual void initialize()
        {
            IsActive = true;
            Color = Color.White;
            IsLeftClicked = false;
            m_children = new Dictionary<string, CWidget>();
            if (this.m_parent != null)
            {
                if (!m_parent.Children.ContainsKey(m_name))
                    this.m_parent.Children.Add(this.m_name, this);
                else
                    m_parent.Children[m_name] = this;
                this.m_isVisible = this.m_parent.IsVisible;
            }
            m_margins = new SPositionsTBLR();
            m_margins.Bottom = 0;
            m_margins.Top = 0;
            m_margins.Right = 0;
            m_margins.Left = 0;
            m_rect = new Rectangle((int)(this.m_position.X * Game.GraphicsDevice.DisplayMode.Width), (int)(this.m_position.Y * Game.GraphicsDevice.DisplayMode.Height), (int)(this.m_size.X * Game.GraphicsDevice.Viewport.Width), (int)(this.m_size.Y * Game.GraphicsDevice.Viewport.Height));
        }

        #endregion

        #region Clear

       /* /// <summary>
        /// Clear the widget by removing all its children from the HUDSystem and by removing them.
        /// </summary>
        public void ClearWidget(bool recursively = true)
        {
            foreach (CWidget wid in m_children.Values)
            {
                if (recursively)
                    wid.ClearWidget(recursively);
                HUD.Remove(wid);
            }
            m_children.Clear();
        }
        */
        #endregion

        #region clone

        /// <summary>
        /// this method clone the widget, the name is the same with "_clone" + Unique number
        /// </summary>
        public virtual CWidget Clone()
        {
            Type t = this.GetType();
            Type[] types = new Type[3] { typeof(CHUDSystem), typeof(CWidget), typeof(string) };
            System.Reflection.ConstructorInfo cinfo = t.GetConstructor(types);
            object[] objects = new object[3] { HUD, m_parent, m_name + "_clone" + m_nbClones };
            CWidget w = cinfo.Invoke(objects) as CWidget;
            ++m_nbClones;
            return Clone(ref w);
        }

        /// <summary>
        /// This method clone the widget, the name is the same with "_clone" + Unique number.
        /// It also registers the cloned widget to the hud given in parameters.
        /// </summary>
        /// <param name="hud">The hud for the widget to be attached to</param>
        public virtual CWidget Clone(CHUDSystem hud)
        {
            Type t = this.GetType();
            Type[] types = new Type[3] { typeof(CHUDSystem), typeof(CWidget), typeof(string) };
            System.Reflection.ConstructorInfo cinfo = t.GetConstructor(types);
            object[] objects = new object[3] { hud, m_parent, m_name + "_clone" + m_nbClones};
            CWidget w = cinfo.Invoke(objects) as CWidget;
            ++m_nbClones;
            return Clone(ref w);
        }

        /// <summary>
        /// Used simplify the Clone function of the classes inheriting CWidget.
        /// On the Clone function, call base.Clone(widgetToBeReturned)
        /// </summary>
        /// <param name="w">the widget constructed that will be returned by the Clone method</param>
        /// <returns>The widget given in parameters</returns>
        protected virtual CWidget Clone(ref CWidget w)
        {
            w.Color = Color;
            w.DrawableRect = DrawableRect;
            w.OnMouseOverEnter = OnMouseOverEnter;
            w.OnMouseOverExit = OnMouseOverExit;
            w.OnClick = OnClick;
            w.InitialPosition = InitialPosition;
            w.IsAlwaysOnTop = IsAlwaysOnTop;
            w.IsLeftClicked = false;
            w.IsContainer = IsContainer;
            w.IsContainerGroup = IsContainerGroup;
            w.IsInputConsumer = IsInputConsumer;
            w.m_isFixedSize = m_isFixedSize;
            w.m_isMoving = false;
            w.m_isVisible = m_isVisible;
            w.m_margins = m_margins;
            //w.MaxSize = MaxSize;
            //w.MinSize = MinSize;
            //CWidget.m_nbClones = 0;
            w.m_position = m_position;
            w.m_rect = m_rect;
            w.OnRelease = OnRelease;
            w.m_size = m_size;
            if (m_texture != null)
                w.m_texture = m_texture.Clone();
            w.UseDrawableRect = UseDrawableRect;
            w.WasMouseOver = false;
            return w;
        }

        #endregion

        #region give Subm_rect / actualize

        /// <summary>
        /// Returns the max width in the widget (in pixels).
        /// </summary>
        /// <returns>Rect.Width - Margins.Left - Margins.Right</returns>
        public virtual float getMaxWidth()
        {
            return m_rect.Width - m_margins.Left - m_margins.Right;
        }

        /// <summary>
        /// Actualizes the widget.
        /// If anything has changed, it will allow this widget to get back its parameters.
        /// For example, if the size of the widget is a percentage of the size of its parent,
        /// if the parent's size changes, it will recalculate the good size of this widget.
        /// (This method is called automatically but you can force a new actualization).
        /// </summary>
        public virtual void Actualize()
        {
            Rectangle tmp = m_rect;
            if (this.m_parent != null && !(m_parent is CListBox))
                this.m_parent.giveSubRect(m_position, m_size, ref m_rect);
            if (m_texture != null)
                m_texture.Rectangle = m_rect;
            if (UseDrawableRect)
            {
                DrawableRect = new Rectangle(DrawableRect.X + m_rect.X - tmp.X, DrawableRect.Y + m_rect.Y - tmp.Y, DrawableRect.Width + m_rect.Width - tmp.Width, DrawableRect.Height + m_rect.Height - tmp.Height);
                m_texture.Rectangle = DrawableRect;
            }
            foreach (CWidget child in m_children.Values)
                child.Actualize();
            foreach (CWidget child in m_children.Values)
                if (child.InitialPosition == null)
                    child.InitialPosition = child.Position;
        }

        /// <summary>
        /// Actualizes the widget.
        /// If anything has changed, it will allow this widget to get back its parameters.
        /// For example, if the size of the widget is a percentage of the size of its parent,
        /// if the parent's size changes, it will recalculate the good size of this widget.
        /// (This method is called automatically but you can force a new actualization).
        /// </summary>
        /// <param name="force">This allow to force the percentage even if the size/position is greater than {1, 1}</param>
        public virtual void Actualize(bool force)
        {
            Rectangle tmp = m_rect;
            if (this.m_parent != null)
                this.m_parent.giveSubRect(m_position, m_size, ref m_rect, force);
            if (m_texture != null)
                m_texture.Rectangle = m_rect;
            if (UseDrawableRect)
            {
                DrawableRect = new Rectangle(DrawableRect.X + m_rect.X - tmp.X, DrawableRect.Y + m_rect.Y - tmp.Y, DrawableRect.Width + m_rect.Width - tmp.Width, DrawableRect.Height + m_rect.Height - tmp.Height);
                m_texture.Rectangle = DrawableRect;
            }
            foreach (CWidget child in m_children.Values)
                child.Actualize();
            foreach (CWidget child in m_children.Values)
                if (child.InitialPosition == null)
                    child.InitialPosition = child.Position;
        }

        /// <summary>
        /// Allow to set to the rectangle given in parameters the position/size relatively to this widget.
        /// When we modify the position or the size of a widget, it will call its parent's giveSubRect method to
        /// get a rectangle that matches its parent.
        /// </summary>
        /// <param name="m_rectangle">the rectangle to be resized</param>
        /// <param name="force">This allow to force the percentage even if the size/position is greater than {1, 1}</param>
        public virtual void giveSubRect(ref Rectangle m_rectangle, bool force)
        {
            SPositionsTBLR oldMargins = m_margins;
            m_margins.Left = 2;
            m_margins.Right = 2;
            m_margins.Top = 2;
            m_margins.Bottom = 2;

            giveSubRect(new Vector2(0, 0), new Vector2(1, 1), ref m_rectangle, force);

            m_margins = oldMargins;
        }

        public virtual void giveSubRect(ref Rectangle m_rectangle)
        {
            giveSubRect(ref m_rectangle, false);
        }

        /// <summary>
        /// Allow to set to the rectangle given in parameters the position/size relatively to this widget.
        /// When we modify the position or the size of a widget, it will call its parent's giveSubRect method to
        /// get a rectangle that matches its parent.
        /// </summary>
        /// <param name="pos">the position relative to this widget</param>
        /// <param name="m_size">the size relative to this widget</param>
        /// <param name="m_rectangle">the rectangle to be resized</param>
        /// <param name="force">This allow to force the percentage even if the size/position is greater than {1, 1}</param>
        public virtual void giveSubRect(Vector2 pos, Vector2 m_size, ref Rectangle m_rectangle, bool force)
        {
            int x;
            int y;
            int width;
            int height;
            if ((m_size.X <= 1 && m_size.Y <= 1) || force)
            {
                width = (int)((m_size.X * m_rect.Width) - (m_margins.Left + m_margins.Right));
                height = (int)((m_size.Y * m_rect.Height) - (m_margins.Top + m_margins.Bottom));
            }
            else
            {
                width = (int)m_size.X;
                height = (int)m_size.Y;
            }
            if ((pos.X <= 1 && pos.X >= 0 && pos.Y <= 1 && pos.Y >= 0) || force)
            {
                x = (int)(m_rect.Left + (pos.X * (m_rect.Width - m_margins.Left - m_margins.Right)) + m_margins.Left);
                y = (int)(m_rect.Top + (pos.Y * (m_rect.Height - m_margins.Top - m_margins.Bottom)) + m_margins.Right);
            }
            else
            {
                x = (int)(m_rect.Left + pos.X + m_margins.Left);
                y = (int)(m_rect.Top + pos.Y + m_margins.Top);
            }
            m_rectangle.X = x;
            m_rectangle.Y = y;
            m_rectangle.Width = width;
            m_rectangle.Height = height;
        }

        public virtual void giveSubRect(Vector2 pos, Vector2 m_size, ref Rectangle m_rectangle)
        {
            giveSubRect(pos, m_size, ref m_rectangle, false);
        }

        #endregion

        #region GetElement
        
        /// <summary>
        /// Check if the given widget is in the parentship of the current widget.
        /// </summary>
        public virtual bool IsInHierarchy(CWidget wid)
        {
            if (Parent == wid)
                return true;
            if (Parent == null)
                return false;
            return Parent.IsInHierarchy(wid);
        }

        /// <summary>
        /// Check if there is any widget of the given type in the parentship of the current widget.
        /// </summary>
        public virtual bool IsInHierarchy(Type type)
        {
            if (Parent == null)
                return false; 
            if (Parent.GetType() == type)
                return true;
            return Parent.IsInHierarchy(type);
        }

        /// <summary>
        /// Gets a widget that intersects with a given rectangle.
        /// </summary>
        public virtual CWidget GetElement(Rectangle pos, Type except)
        {
            CWidget ret = null;

            foreach (CWidget child in m_children.Values)
            {
                if (child.IsVisible && child.GetType() != except && child.isOver(pos))
                {
                    ret = child;
                    break;
                }
            }
            if (ret == null)
                return this;
            return ret;
        }

        /// <summary>
        /// Gets a widget that intersects with a given rectangle.
        /// </summary>
        public virtual CWidget GetElement(Rectangle pos, CWidget except)
        {
            CWidget ret = null;

            foreach (CWidget child in m_children.Values)
            {
                if (child.IsVisible && child != except && child.isOver(pos))
                {
                    ret = child;
                    break;
                }
            }
            if (ret == null)
                return this;
            return ret;
        }

        public virtual CWidget GetElement(Rectangle pos)
        {
            return GetElement(pos, (CWidget)null);
        }

        /// <summary>
        /// Gets a widget by its name
        /// </summary>
        public virtual CWidget GetElement(string m_name)
        {
            if (m_name == this.m_name)
                return this;
            CWidget ret = null;
            foreach (CWidget child in m_children.Values)
            {
                CWidget tmp = child.GetElement(m_name);
                if (tmp != null)
                {
                    ret = tmp;
                    break;
                }
            }
            return ret;
        }

        /// <summary>
        /// Gets a widget that is under the cursor
        /// </summary>
        public virtual CWidget GetElement(CMouse mouse, CWidget except)
        {
            CWidget ret = null;
            foreach (CWidget child in m_children.Values)
                if (child.IsVisible && child != except && child.isMouseOver(mouse))
                {
                    ret = child.GetElement(mouse, except);
                    break;
                }
            if (ret != null)
                return ret;
            return this;
        }

        public virtual CWidget GetElement(CMouse mouse)
        {
            return GetElement(mouse, (CWidget)null);
        }

        /// <summary>
        /// Gets a widget that is under the cursor.
        /// </summary>
        public virtual CWidget GetElement(CMouse mouse, Type except)
        {
            CWidget ret = null;
            foreach (CWidget child in m_children.Values)
                if (child.IsVisible && child.GetType() != except && child.isMouseOver(mouse))
                {
                    ret = child.GetElement(mouse, except);
                    break;
                }
            if (ret != null)
                return ret;
            return this;
        }

        #endregion
    
        #region show hide methods
        
        /// <summary>
        /// Shows this widget and all its children
        /// </summary>
        public virtual void Show()
        {
            this.m_isVisible = true;
            foreach (CWidget child in m_children.Values)
                child.Show();
        }

        /// <summary>
        /// Hides this widget and all its children
        /// </summary>
        public virtual void Hide()
        {
            this.m_isVisible = false;
            foreach (CWidget child in m_children.Values)
                child.Hide();
        }

        /// <summary>
        /// Allow to toogle the visibility of the widget
        /// </summary>
        public virtual void ToogleVisibility()
        {
            if (m_isVisible)
                Hide();
            else
                Show();
        }

        #endregion

        #region events

        private void InvokeEvent(eventMethod e, CWidget sender)
        {
            if (e != null)
                e.Invoke(sender);
        }

        /// <summary>
        /// Raises the OnClick event
        /// </summary>
        public void InvokeClick(CWidget sender)
        {
            InvokeEvent(OnClick, sender);
        }

        /// <summary>
        /// Raises the OnRelease event
        /// </summary>
        public void InvokeRelease(CWidget sender)
        {
            InvokeEvent(OnRelease, sender);
        }

        /// <summary>
        /// Raises the OnMouseOverEnter event
        /// </summary>
        public void InvokeMouseOverEnter(CWidget sender)
        {
            InvokeEvent(OnMouseOverEnter, sender);
        }

        /// <summary>
        /// Raises the OnMouseOverExit event
        /// </summary>
        public void InvokeMouseOverExit(CWidget sender)
        {
            InvokeEvent(OnMouseOverExit, sender);
        }



        /// <summary>
        /// Checks if the rectangle given in parameters is over this widget
        /// </summary>
        public virtual bool isOver(Rectangle pos)
        {
            if (pos.Intersects(m_rect))
                return true;
            return false;
        }

        /// <summary>
        /// Checks if the CMouse given in parameters is over this widget
        /// </summary>
        public virtual bool isMouseOver(CMouse mouse)
        {
            if (HUD.Cursor.IntersectionRect.Intersects(m_rect))
                return true;
            /*if (mouse.Position.X >= m_rect.Left - 3 && mouse.Position.X <= m_rect.Right + 3 && mouse.Position.Y <= m_rect.Bottom + 3 && mouse.Position.Y >= m_rect.Top - 3)
                return true;*/
            return false;
        }

        /// <summary>
        /// Method called whenever the mouse is getting over the widget.
        /// </summary>
        public virtual bool onMouseOverEnter(CMouse mouse)
        {
            foreach (CWidget child in m_children.Values)
                if (child.IsVisible && child.isMouseOver(mouse))
                {
                    m_selectedWidget = child;
                    child.onMouseOverEnter(mouse);
                    break;
                }
            if (!WasMouseOver)
            {
                if (OnMouseOverEnter != null)
                    OnMouseOverEnter(this);
                WasMouseOver = true;
                return true;
            }
            return false;
        }

        /// <summary>
        /// Method called whenever the mouse is getting out the widget.
        /// </summary>
        public virtual bool onMouseOverExit(CMouse mouse)
        {
            if (!WasMouseOver)
                return false;
            if (m_selectedWidget != null)
                if (m_selectedWidget.onMouseOverExit(mouse))
                    m_selectedWidget = null;
            if (!isMouseOver(mouse))
            {
                if (OnMouseOverExit != null)
                    OnMouseOverExit(this);
                WasMouseOver = false;
                return true;
            }
            return false;
        }

        /// <summary>
        /// Method called whenever the mouse is pressed and is over this widget
        /// </summary>
        public virtual bool onClick(CMouse mouse)
        {
            if (mouse.ButtonPressedEvent.First() == CMouse.EButton.LEFT)
                IsLeftClicked = true;
            else if (mouse.ButtonPressedEvent.First() == CMouse.EButton.RIGHT)
                IsRightClicked = true;
            else if (mouse.ButtonPressedEvent.First() == CMouse.EButton.MIDDLE)
                IsMiddleClicked = true;
            bool someoneClicked = false;
            // Check if one childrens is clicked on
            foreach (CWidget child in m_children.Values)
                if (child.IsVisible && child.isMouseOver(mouse))
                {
                    someoneClicked = true;
                    child.onClick(mouse);
                    m_activatedCWidget = child;
                    break;
                }
            // If nobody is clicked on, I am. So execute this OnClick delegate.
            if (!someoneClicked || this as CButton != null)
            {
                if (OnClick != null)
                    OnClick(this);
                return false;
            }
            
            // Return to the m_parent false if I'm clicked on or true if one of my childrens is clicked on.
            return someoneClicked;
        }

        /// <summary>
        /// Method called whenever the mouse is released and is over this widget and was pressed over this widget
        /// </summary>
        public virtual bool onRelease(CMouse mouse)
        {

            bool ret = IsLeftClicked;
            bool someoneClicked = false;
            if (m_activatedCWidget != null)
            {
                m_activatedCWidget.onRelease(mouse);
                m_activatedCWidget = null;
                someoneClicked = true;
            }
            if ((!someoneClicked && IsLeftClicked && isMouseOver(mouse)) || (this as CButton != null && IsLeftClicked && isMouseOver(mouse)))
            {
                if (OnRelease != null)
                    OnRelease(this);
            }
            if (mouse.ButtonReleasedEvent.First() == CMouse.EButton.LEFT)
                IsLeftClicked = false;
            else if (mouse.ButtonReleasedEvent.First() == CMouse.EButton.RIGHT)
                IsRightClicked = false;
            else if (mouse.ButtonReleasedEvent.First() == CMouse.EButton.MIDDLE)
                IsMiddleClicked = false;
            return ret;
        }

        #endregion

        #region update

        public virtual bool Update(GameTime m_gameTime)
        {
            if (!this.m_isVisible)
                return false;
            for (int i = 0; i < m_children.Count; ++i)
                m_children.Values.ElementAt(i).Update(m_gameTime);

            // tout ca a virer et foutre un shader2D
            m_highLightRectangle = m_rect;
            m_highLightRectangle.X -= 5;
            m_highLightRectangle.Y -= 5;
            m_highLightRectangle.Width += 10;
            m_highLightRectangle.Height += 10;

            if (m_highLightChanged)
            {
                m_startTime = (float)m_gameTime.TotalGameTime.TotalMilliseconds;
            }
            m_highLightChanged = false;
            if (m_gameTime.TotalGameTime.TotalMilliseconds > m_startTime + m_duration)
            {
                m_highLightAlphaWhite = new Color(0, 0, 0, 0);
            }
            else
            {
                float value;
                if (m_loopEnabled)
                    value = MathHelper.Lerp(m_minHighLightIntensity, m_maxHighLightIntensity, (float)((Math.Sin(m_gameTime.TotalGameTime.TotalMilliseconds) + 1) / 2.0f));
                else
                    value = MathHelper.Lerp(m_minHighLightIntensity, m_maxHighLightIntensity, (float)((m_startTime + m_duration) / m_gameTime.TotalGameTime.TotalMilliseconds));
                m_highLightAlphaWhite = new Color(value * 255, value * 255, value * 255, value * 255);
            }

            return true;
        }

        #endregion

        #region utils

        #region a mettre dans un shader 2D

        // tout ca a virer et foutre un shader2D
        Texture2D m_highLightTexture;
        Rectangle m_highLightRectangle;
        Color m_highLightAlphaWhite;
        float m_startTime;
        float m_duration;
        bool m_highLightChanged = false;
        bool m_loopEnabled = false;
        float m_minHighLightIntensity;
        float m_maxHighLightIntensity;
        // tout ca a virer et foutre un shader2D
        public Texture2D HightLightTexture { get { return m_highLightTexture; } set { m_highLightTexture = value; } }

        /// <summary>
        /// // tout ca a virer et foutre un shader2D
        /// // tout ca a virer et foutre un shader2D
        /// // tout ca a virer et foutre un shader2D
        /// // tout ca a virer et foutre un shader2D
        /// // tout ca a virer et foutre un shader2D
        /// // tout ca a virer et foutre un shader2D
        /// 
        /// </summary>
        /// <param name="minIntensity">between 1 and 0, represents the min alpha</param>
        /// <param name="maxIntensity">between 1 and 0, represents the max alpha</param>
        /// <param name="duration"></param>
        /// <param name="loopEnabled"></param>
        public void HighLight(float minIntensity, float maxIntensity, float duration, bool loopEnabled)
        {
            m_highLightRectangle = m_rect;
            m_highLightRectangle.X-=5;
            m_highLightRectangle.Y-=5;
            m_highLightRectangle.Width+=10;
            m_highLightRectangle.Height+=10;
            m_highLightAlphaWhite = new Color(minIntensity * 255, minIntensity * 255, minIntensity * 255, minIntensity * 255);
            m_duration = duration;
            m_highLightChanged = true;
            m_minHighLightIntensity = minIntensity;
            m_maxHighLightIntensity = maxIntensity;
            m_loopEnabled = loopEnabled;
        }

        #endregion

        /// <summary>
        /// Clear the widget by removing all its children from the HUDSystem and by removing them.
        /// </summary>
        public virtual void Clear(bool recursively)
        {
            if (recursively)
                foreach (CWidget wid in m_children.Values)
                    wid.Clear(recursively);
            HUD.Widgets.Remove(Name);
            HUD.DisplayList.Remove(this);
        }

        public virtual void Clear()
        {
            Clear(true);
        }

        /// <summary>
        /// This attachs a containable widget to a container in a widget that is a container group (`IsContainerGroup = true`)
        /// </summary>
        /// <param name="from">This is the widget to be attached</param>
        /// <param name="to">Can be a cumulable widget to which we want to attach the "from" widget. For example, if you drop a Vignette on another representing the same item, and if this vignette is cumulable, set the give the vignette on which you will attach the first one.</param>
        public virtual bool Attach(CWidget from, CWidget to)
        {
            foreach (CWidget child in m_children.Values)
                if (child.Attach(from, to))
                    return true;
            return false;
        }

        public virtual bool Attach(CWidget from)
        {
            return Attach(from, null);
        }

        #endregion

        #region draw

        public virtual bool Draw(GameTime gameTime)
        {
            if (!this.m_isVisible)
                return false;
            Color old = HUD.Shader.HighLightColor;
            bool wasHL = HUD.Shader.IsHighLighted; 
            if (IsHighLighted)
            {
                HUD.Shader.IsHighLighted = IsHighLighted;
                HUD.Shader.HighLightColor = HLColor;
            }
            if (this.m_texture != null)
            {
                if (m_texture.Rectangle != m_rect)
                    m_texture.Rectangle = m_rect;
                m_texture.Draw(gameTime);
                /*m_sourceRect = new Rectangle(0, 0, m_texture.Width, m_texture.Height);
                if (m_useDrawableRect)
                    m_spriteBatch.Draw(this.m_texture, m_drawableRect, m_sourceRect, m_color);
                else
                    m_spriteBatch.Draw(this.m_texture, m_rect, m_sourceRect, m_color);*/
            }
            for (int i = 0; i < m_children.Count; ++i)
            {
                CWidget child = m_children.Values.ElementAt(i);
                if (child.IsAlwaysOnTop || m_rect.Intersects(child.Rect))
                    child.Draw(gameTime);
            }
            if (IsHighLighted)
                HUD.Shader.HighLightColor = old;
            HUD.Shader.IsHighLighted = wasHL;
            return true;
        }

        #endregion

    }
}
